| File: PooledArrayBufferWriter`1.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj (Microsoft.AspNetCore.Razor.Utilities.Shared) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Copied from https://github.com/dotnet/aspnetcore/blob/207bc7d00c00bf454dc83789d6824609b1d8b02a/src/Shared/PooledArrayBufferWriter.cs, // which is derived from https://github.com/dotnet/runtime/blob/abb5c348f242e546b5768ad70ebe4051f08f7d24/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Razor; namespace System.Buffers; /// <summary> /// <see cref="IBufferWriter{T}"/> that uses <see cref="ArrayPool{T}"/>. /// </summary> internal sealed class PooledArrayBufferWriter<T> : IBufferWriter<T>, IDisposable { private T[]? _rentedBuffer; private int _index; private const int MinimumBufferSize = 256; public PooledArrayBufferWriter() { _rentedBuffer = ArrayPool<T>.Shared.Rent(MinimumBufferSize); _index = 0; } public PooledArrayBufferWriter(int initialCapacity) { ArgHelper.ThrowIfNegativeOrZero(initialCapacity); _rentedBuffer = ArrayPool<T>.Shared.Rent(initialCapacity); _index = 0; } public ReadOnlyMemory<T> WrittenMemory { get { CheckIfDisposed(); return _rentedBuffer.AsMemory(0, _index); } } public int WrittenCount { get { CheckIfDisposed(); return _index; } } public int Capacity { get { CheckIfDisposed(); return _rentedBuffer.Length; } } public int FreeCapacity { get { CheckIfDisposed(); return _rentedBuffer.Length - _index; } } public void Clear() { CheckIfDisposed(); ClearHelper(); } private void ClearHelper() { Debug.Assert(_rentedBuffer != null); _rentedBuffer.AsSpan(0, _index).Clear(); _index = 0; } // Returns the rented buffer back to the pool public void Dispose() { if (_rentedBuffer == null) { return; } ClearHelper(); ArrayPool<T>.Shared.Return(_rentedBuffer); _rentedBuffer = null; } [MemberNotNull(nameof(_rentedBuffer))] private void CheckIfDisposed() { if (_rentedBuffer == null) { ThrowObjectDisposed(); } [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] static void ThrowObjectDisposed() { throw new ObjectDisposedException(nameof(ArrayBufferWriter<T>)); } } public void Advance(int count) { CheckIfDisposed(); ArgHelper.ThrowIfNegative(count); if (_index > _rentedBuffer.Length - count) { ThrowCannotAdvance(_rentedBuffer.Length); } _index += count; [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] static void ThrowCannotAdvance(int capacity) { throw new InvalidOperationException(SR.FormatCannot_advance_past_end_of_the_buffer_which_has_a_size_of_0(capacity)); } } public Memory<T> GetMemory(int sizeHint = 0) { CheckIfDisposed(); CheckAndResizeBuffer(sizeHint); return _rentedBuffer.AsMemory(_index); } public Span<T> GetSpan(int sizeHint = 0) { CheckIfDisposed(); CheckAndResizeBuffer(sizeHint); return _rentedBuffer.AsSpan(_index); } private void CheckAndResizeBuffer(int sizeHint) { Debug.Assert(_rentedBuffer != null); ArgHelper.ThrowIfNegative(sizeHint); if (sizeHint == 0) { sizeHint = MinimumBufferSize; } var availableSpace = _rentedBuffer!.Length - _index; if (sizeHint > availableSpace) { var growBy = Math.Max(sizeHint, _rentedBuffer.Length); var newSize = checked(_rentedBuffer.Length + growBy); var oldBuffer = _rentedBuffer; _rentedBuffer = ArrayPool<T>.Shared.Rent(newSize); Debug.Assert(oldBuffer.Length >= _index); Debug.Assert(_rentedBuffer.Length >= _index); var previousBuffer = oldBuffer.AsSpan(0, _index); previousBuffer.CopyTo(_rentedBuffer); previousBuffer.Clear(); ArrayPool<T>.Shared.Return(oldBuffer); } Debug.Assert(_rentedBuffer.Length - _index > 0); Debug.Assert(_rentedBuffer.Length - _index >= sizeHint); } }